/**
 * Copyright 2010 Philippe Beaudoin
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.puzzlebazar.client.ui;

import java.util.NoSuchElementException;

import com.google.gwt.uibinder.client.UiConstructor;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.InlineHTML;
import com.google.gwt.user.client.ui.Widget;

/**
 * @author Philippe Beaudoin
 */
public class TokenSeparatedList extends FlowPanel  {

  private final String token;

  private int leftmostVisibleIndex;

  @UiConstructor
  public TokenSeparatedList(String token) {
    this.token = token;
  }

  /**
   * Adds a widget to the token separated list. You should
   * only add inline widgets (not block widget), such as
   * {@link com.google.gwt.user.client.ui.Anchor}, {@link com.google.gwt.user.client.ui.InlineLabel},
   * {@link com.google.gwt.user.client.ui.InlineHyperlink}
   * or {@link com.google.gwt.user.client.ui.InlineHTML}.
   *
   * @param widget The widget to add.
   */
  @Override
  public void add(Widget widget) {
    if (getWidgetCount() > 0) {
      super.add(new InlineHTML(token));
    }
    super.add(widget);
    updateVisibility();
  }

  /**
   * Adds a widget to the token separated list before the specified
   * widget. You should only add inline widgets (not block widget),
   * such as {@link com.google.gwt.user.client.ui.Anchor}, {@link com.google.gwt.user.client.ui.InlineLabel}, {@link
   * com.google.gwt.user.client.ui.InlineHyperlink} or {@link com.google.gwt.user.client.ui.InlineHTML}.
   *
   * @param widgetToAdd The widget to add.
   * @param nextWidget The widget before which to add. Pass <code>null</code> to insert at the end.
   */
  public void addBefore(Widget widgetToAdd, Widget nextWidget) {
    if (nextWidget == null) {
      add(widgetToAdd);
      return;
    }
    int index = getWidgetIndex(nextWidget);
    if (index < 0) {
      throw new NoSuchElementException("Next widget not found.");
    }
    insert(new InlineHTML(token), index);
    insert(widgetToAdd, index);
    updateVisibility();
  }

  /**
   * Call this to make a widget visible or invisible. You should not call
   * setVisible() directly on the widget, otherwise its separating token
   * will be lost.
   *
   * @param widget The widget on which to change visibility.
   * @param visible The desired visibility.
   */
  public void setWidgetVisible(Widget widget, boolean visible) {
    int index = getWidgetIndex(widget);
    if (index < 0) {
      throw new NoSuchElementException("Widget not found.");
    }
    widget.setVisible(visible);

    // Check special cases
    if (index < leftmostVisibleIndex && visible) {
      // Making a widget visible to the left of the current leftmost visible widget.
      // or making the leftmost visible widget visible again.

      // Make visible the separator of the previous leftmost visible widget, unless none were visible.
      if (leftmostVisibleIndex < getWidgetCount()) {
        getWidget(leftmostVisibleIndex - 1).setVisible(true);
      }
      leftmostVisibleIndex = index;
    } else if (index == leftmostVisibleIndex && !visible) {
      // Making the leftmost visible widget invisible.

      // Find the new leftmost visible widget.
      leftmostVisibleIndex += 2;
      while (leftmostVisibleIndex < getWidgetCount()) {
        if (getWidget(leftmostVisibleIndex).isVisible()) {
          break;
        }
        leftmostVisibleIndex += 2;
      }

      // Make invisible the separator of the new leftmost visible widget, unless they are all invisible
      if (leftmostVisibleIndex < getWidgetCount()) {
        getWidget(leftmostVisibleIndex - 1).setVisible(false);
      }
    } else if (index > leftmostVisibleIndex) {
      // Typical case, apply the same visibility to the widget's separator.
      getWidget(index - 1).setVisible(visible);
    }
  }

  /**
   * Ensures the visibility status of all widget is right.
   */
  private void updateVisibility() {
    if (getWidget(0).isVisible()) {
      leftmostVisibleIndex = 0;
    } else {
      leftmostVisibleIndex = getWidgetCount();
    }

    for (int index = 2; index < getWidgetCount(); index += 2) {
      if (getWidget(index).isVisible()) {
        if (leftmostVisibleIndex == getWidgetCount()) {
          leftmostVisibleIndex = index;
        } else {
          getWidget(index - 1).setVisible(true);
        }
      } else {
        getWidget(index - 1).setVisible(false);
      }
    }
  }
}